package com.ruoyi.common.utils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 图片对比并重命名 （王芳外国人采集）
 */
public class PreciseHashImageMatcher {

    public static void main(String[] args) {
        // 设置目录参数
        File cleanedDir = new File("C:\\Users\\lenovo\\Downloads\\美图设计室_变清晰_2025_07_14"); // 清晰处理图片目录
        File originalDir = new File("C:\\Users\\lenovo\\Desktop\\修改"); // 原图目录
        File renamedDir = new File("C:\\Users\\lenovo\\Desktop\\重命名结果"); // 新增：重命名后的文件目录
        File backupDir = new File("C:\\Users\\lenovo\\Desktop\\backup"); // 冲突备份目录

        // 确保所有目录都存在
        if (!renamedDir.exists()) renamedDir.mkdirs();
        if (!backupDir.exists()) backupDir.mkdirs();

        // 日志记录器
        Logger logger = new Logger(renamedDir);

        try {
            // 获取两组图片文件
            List<File> cleanedImages = listImageFiles(cleanedDir);
            List<File> originalImages = listImageFiles(originalDir);
            logger.log("[INFO] 清晰处理图片数量: " + cleanedImages.size());
            logger.log("[INFO] 原始图片数量: " + originalImages.size());

            // 存储匹配结果和未匹配文件
            Map<File, String> renameMap = new HashMap<>();
            List<File> unmatchedCleaned = new ArrayList<>(cleanedImages);

            // 预计算所有原图的多种哈希值
            logger.log("[STEP] 计算原图哈希值...");
            Map<File, ImageHashes> originalHashes = new HashMap<>();
            for (File original : originalImages) {
                try {
                    originalHashes.put(original, computeAllHashes(original));
                    logger.log("  [HASH] 原图: " + original.getName());
                } catch (Exception e) {
                    logger.log("[WARN] 原图哈希计算失败: " + original.getName() + " - " + e.getMessage());
                }
            }

            // 匹配图片并构建重命名映射
            logger.log("[STEP] 开始图片匹配...");
            for (File cleaned : cleanedImages) {
                try {
                    ImageHashes cleanedHashes = computeAllHashes(cleaned);
                    File bestMatch = null;
                    double bestSimilarity = 0;

                    for (Map.Entry<File, ImageHashes> entry : originalHashes.entrySet()) {
                        File original = entry.getKey();
                        ImageHashes origHashes = entry.getValue();

                        // 计算综合相似度分数
                        double similarityScore = computeSimilarityScore(cleanedHashes, origHashes);

                        // 记录最佳匹配
                        if (similarityScore > bestSimilarity) {
                            bestSimilarity = similarityScore;
                            bestMatch = original;
                        }
                    }

                    // 阈值判定：相似度≥85%视为匹配成功
                    if (bestMatch != null && bestSimilarity >= 0.85) {
                        renameMap.put(cleaned, bestMatch.getName());
                        unmatchedCleaned.remove(cleaned);
                        logger.log(String.format("  [MATCH] 匹配成功: %s -> %s (相似度: %.2f%%)",
                                cleaned.getName(), bestMatch.getName(), bestSimilarity * 100));
                    } else if (bestMatch != null) {
                        logger.log(String.format("  [PARTIAL] 部分匹配: %s -> %s (相似度: %.2f%%)",
                                cleaned.getName(), bestMatch.getName(), bestSimilarity * 100));
                    } else {
                        logger.log("  [UNMATCHED] 无匹配: " + cleaned.getName());
                    }
                } catch (Exception e) {
                    logger.log("[ERROR] 处理失败: " + cleaned.getName() + " - " + e.getMessage());
                }
            }

            // 执行重命名操作
            logger.log("\n[STEP] 执行重命名操作...");
            int renameCount = performRenaming(renameMap, cleanedDir, renamedDir, backupDir, logger);

            // 输出统计结果
            logger.log("\n[RESULT] ========== 任务完成 ==========");
            logger.log("成功匹配并重命名: " + renameCount + " 张图片");
            logger.log("已移动到目录: " + renamedDir.getAbsolutePath());
            logger.log("未匹配的清晰图片: " + unmatchedCleaned.size());
            if (!unmatchedCleaned.isEmpty()) {
                logger.log("未匹配文件列表:");
                unmatchedCleaned.forEach(f -> logger.log("  - " + f.getName()));
            }
        } catch (Exception e) {
            logger.log("[FATAL] 程序发生严重错误: " + e.getMessage());
            e.printStackTrace();
        } finally {
            logger.close();
        }
    }

    /**
     * 执行重命名操作
     */
    private static int performRenaming(Map<File, String> renameMap,
                                       File sourceDir, File targetDir,
                                       File backupDir, Logger logger) {
        int renameCount = 0;

        for (Map.Entry<File, String> entry : renameMap.entrySet()) {
            File cleanedFile = entry.getKey();
            String newName = sanitizeFilename(entry.getValue());
            File newFile = new File(targetDir, newName);

            try {
                // 处理目标文件名冲突
                if (newFile.exists()) {
                    File backupFile = generateUniqueBackupFile(backupDir, newName);
                    if (safeMoveFile(newFile, backupFile)) {
                        logger.log("  [CONFLICT] 文件名冲突已备份: " + newFile.getName() + " -> " + backupFile.getName());
                    } else {
                        logger.log("  [ERROR] 冲突文件备份失败: " + newFile.getName());
                    }
                }

                // 执行重命名和移动操作
                if (safeRenameAndMove(cleanedFile, newFile)) {
                    renameCount++;
                    logger.log("  [RENAME] 已重命名并移动: " + cleanedFile.getName() + " → " + newFile.getName());
                } else {
                    // 尝试复制方式
                    if (safeCopyFile(cleanedFile, newFile)) {
                        renameCount++;
                        logger.log("  [COPY] 已复制文件: " + cleanedFile.getName() + " → " + newFile.getName());
                    } else {
                        logger.log("  [ERROR] 重命名失败: " + cleanedFile.getName());
                    }
                }
            } catch (Exception e) {
                logger.log("  [ERROR] 重命名异常: " + cleanedFile.getName() + " - " + e.getMessage());
            }
        }
        return renameCount;
    }

    /**
     * 安全重命名并移动文件
     */
    private static boolean safeRenameAndMove(File source, File target) {
        final int MAX_RETRIES = 3;
        final int RETRY_DELAY_MS = 500;

        for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
            try {
                // 创建目标目录（如果不存在）
                if (!target.getParentFile().exists()) {
                    target.getParentFile().mkdirs();
                }

                // 尝试直接移动
                if (source.renameTo(target)) {
                    return true;
                }

                // 尝试使用Files.move
                try {
                    Files.move(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
                    return true;
                } catch (IOException ex) {
                    // 继续尝试其他方法
                }

                // 尝试复制+删除方式
                if (safeCopyFile(source, target)) {
                    if (source.delete()) {
                        return true;
                    }
                }

                // 等待后重试
                if (attempt < MAX_RETRIES) {
                    TimeUnit.MILLISECONDS.sleep(RETRY_DELAY_MS);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            } catch (Exception e) {
                // 记录错误但不中断重试
            }
        }
        return false;
    }

    /**
     * 安全复制文件
     */
    private static boolean safeCopyFile(File source, File target) {
        try {
            Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    /**
     * 安全移动文件
     */
    private static boolean safeMoveFile(File source, File target) {
        try {
            Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
            return source.delete();
        } catch (IOException e) {
            return false;
        }
    }

    /**
     * 生成唯一的备份文件名
     */
    private static File generateUniqueBackupFile(File backupDir, String baseName) {
        String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String randomPart = String.valueOf((int)(Math.random() * 1000));
        String safeName = sanitizeFilename(baseName);
        File candidate = new File(backupDir, "backup_" + timestamp + "_" + randomPart + "_" + safeName);

        // 如果意外存在，添加更多随机性
        if (candidate.exists()) {
            randomPart = String.valueOf((int)(Math.random() * 10000));
            candidate = new File(backupDir, "backup_" + timestamp + "_" + randomPart + "_" + safeName);
        }
        return candidate;
    }

    /**
     * 文件名规范化（移除非法字符）
     */
    private static String sanitizeFilename(String filename) {
        // 移除非法字符（Windows）
        String sanitized = filename.replaceAll("[\\\\/:*?\"<>|]", "_");

        // 限制文件名长度
        int maxLength = 240;
        if (sanitized.length() > maxLength) {
            String ext = "";
            int dotIndex = sanitized.lastIndexOf('.');
            if (dotIndex > 0) {
                ext = sanitized.substring(dotIndex);
                sanitized = sanitized.substring(0, dotIndex);
            }

            sanitized = sanitized.substring(0, Math.min(sanitized.length(), maxLength - ext.length()));
            sanitized += ext;
        }

        return sanitized;
    }

    /**
     * 获取目录中的图片文件列表
     */
    private static List<File> listImageFiles(File dir) {
        File[] files = dir.listFiles((d, name) ->
                name.toLowerCase().matches(".*\\.(jpg|jpeg|png|gif|bmp|webp)"));
        return files != null ? new ArrayList<>(Arrays.asList(files)) : new ArrayList<>();
    }

    /**
     * 计算所有哈希值
     */
    private static ImageHashes computeAllHashes(File imageFile) throws IOException {
        BufferedImage img = ImageIO.read(imageFile);
        if (img == null) {
            throw new IOException("无法读取图像文件");
        }
        return new ImageHashes(
                averageHash(img),
                perceptualHash(img),
                differenceHash(img),
                colorHistogramHash(img),
                edgeDetectionHash(img)
        );
    }

    /**
     * 图像哈希值容器类
     */
    static class ImageHashes {
        long avgHash;
        long phash;
        long dhash;
        long colorHistHash;
        long edgeDetectHash;

        public ImageHashes(long avgHash, long phash, long dhash, long colorHistHash, long edgeDetectHash) {
            this.avgHash = avgHash;
            this.phash = phash;
            this.dhash = dhash;
            this.colorHistHash = colorHistHash;
            this.edgeDetectHash = edgeDetectHash;
        }
    }

    /**
     * 计算综合相似度分数
     */
    private static double computeSimilarityScore(ImageHashes h1, ImageHashes h2) {
        // 使用加权平均计算综合相似度
        double similaritySum = 0;

        // 分配权重
        double avgWeight = 0.15;   // 平均哈希权重
        double phashWeight = 0.30; // 感知哈希权重（最重要）
        double dhashWeight = 0.25; // 差异哈希权重
        double colorWeight = 0.20; // 颜色直方图权重
        double edgeWeight = 0.10;  // 边缘检测权重

        // 计算各项相似度
        similaritySum += (1 - normalizedHamming(h1.avgHash, h2.avgHash)) * avgWeight;
        similaritySum += (1 - normalizedHamming(h1.phash, h2.phash)) * phashWeight;
        similaritySum += (1 - normalizedHamming(h1.dhash, h2.dhash)) * dhashWeight;
        similaritySum += (1 - normalizedHamming(h1.colorHistHash, h2.colorHistHash)) * colorWeight;
        similaritySum += (1 - normalizedHamming(h1.edgeDetectHash, h2.edgeDetectHash)) * edgeWeight;

        return similaritySum;
    }

    /**
     * 归一化汉明距离（0-1，0表示相同）
     */
    private static double normalizedHamming(long hash1, long hash2) {
        int distance = Long.bitCount(hash1 ^ hash2);
        return distance / 64.0; // 64位哈希的最大距离为64
    }

    /**
     * 平均哈希 (aHash)
     */
    private static long averageHash(BufferedImage img) {
        // 1. 缩放至8x8
        BufferedImage resized = resizeImage(img, 8, 8);

        // 2. 计算平均灰度
        long total = 0;
        int width = resized.getWidth();
        int height = resized.getHeight();
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                total += resized.getRGB(x, y) & 0xFF;
            }
        }
        int avg = (int) (total / (width * height));

        // 3. 生成64位哈希
        long hash = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                int pixel = resized.getRGB(x, y) & 0xFF;
                hash = (hash << 1) | (pixel > avg ? 1 : 0);
            }
        }
        return hash;
    }

    /**
     * 感知哈希 (pHash)
     */
    private static long perceptualHash(BufferedImage img) {
        // 1. 缩放至32x32并转为灰度图
        BufferedImage resized = resizeImage(img, 32, 32);

        // 2. 计算DCT(此处简化为平均灰度)
        long total = 0;
        int width = resized.getWidth();
        int height = resized.getHeight();
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                total += resized.getRGB(x, y) & 0xFF;
            }
        }
        int avg = (int) (total / (width * height));

        // 3. 生成64位哈希（基于平均值比较）
        long hash = 0;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                int pixel = resized.getRGB(x, y) & 0xFF;
                hash = (hash << 1) | (pixel > avg ? 1 : 0);
            }
        }
        return hash;
    }

    /**
     * 差异哈希 (dHash)
     */
    private static long differenceHash(BufferedImage img) {
        // 1. 缩放至9x8（宽度比高度多1，用于比较相邻像素）
        BufferedImage resized = resizeImage(img, 9, 8);

        // 2. 计算水平方向相邻像素差异
        long hash = 0;
        for (int y = 0; y < 8; y++) {
            for (int x = 0; x < 8; x++) {
                int left = resized.getRGB(x, y) & 0xFF;
                int right = resized.getRGB(x + 1, y) & 0xFF;
                hash = (hash << 1) | (left > right ? 1 : 0);
            }
        }
        return hash;
    }

    /**
     * 颜色直方图哈希
     */
    private static long colorHistogramHash(BufferedImage img) {
        // 1. 缩放至16x16
        BufferedImage resized = resizeImage(img, 16, 16);

        // 2. 计算RGB直方图（简化版）
        int[] histogram = new int[64]; // 4x4x4 = 64 bins

        for (int y = 0; y < resized.getHeight(); y++) {
            for (int x = 0; x < resized.getWidth(); x++) {
                int rgb = resized.getRGB(x, y);
                int r = (rgb >> 16) & 0xFF;
                int g = (rgb >> 8) & 0xFF;
                int b = rgb & 0xFF;

                // 量化到4级 (0-3)
                int rBin = r / 64;
                int gBin = g / 64;
                int bBin = b / 64;

                // 计算直方图索引
                int index = (rBin << 4) | (gBin << 2) | bBin;
                histogram[index]++;
            }
        }

        // 3. 生成哈希（基于直方图分布）
        long hash = 0;
        double avg = Arrays.stream(histogram).average().orElse(0);

        for (int count : histogram) {
            hash = (hash << 1) | (count > avg ? 1 : 0);
        }

        return hash;
    }

    /**
     * 边缘检测哈希
     */
    private static long edgeDetectionHash(BufferedImage img) {
        // 1. 缩放至32x32并转为灰度
        BufferedImage resized = resizeImage(img, 32, 32);

        // 2. 应用Sobel边缘检测
        BufferedImage edgeImage = new BufferedImage(32, 32, BufferedImage.TYPE_BYTE_GRAY);
        for (int y = 1; y < 31; y++) {
            for (int x = 1; x < 31; x++) {
                int gx = (
                        (-1 * (resized.getRGB(x-1, y-1) & 0xFF)) +
                                (-2 * (resized.getRGB(x-1, y) & 0xFF)) +
                                (-1 * (resized.getRGB(x-1, y+1) & 0xFF)) +
                                (1 * (resized.getRGB(x+1, y-1) & 0xFF)) +
                                (2 * (resized.getRGB(x+1, y) & 0xFF)) +
                                (1 * (resized.getRGB(x+1, y+1) & 0xFF))
                );

                int gy = (
                        (-1 * (resized.getRGB(x-1, y-1) & 0xFF)) +
                                (-2 * (resized.getRGB(x, y-1) & 0xFF)) +
                                (-1 * (resized.getRGB(x+1, y-1) & 0xFF)) +
                                (1 * (resized.getRGB(x-1, y+1) & 0xFF)) +
                                (2 * (resized.getRGB(x, y+1) & 0xFF)) +
                                (1 * (resized.getRGB(x+1, y+1) & 0xFF))
                );

                int gradient = (int) Math.sqrt(gx * gx + gy * gy);
                gradient = Math.min(255, gradient); // 限制在0-255范围内
                edgeImage.setRGB(x, y, (gradient << 16) | (gradient << 8) | gradient);
            }
        }

        // 3. 生成基于边缘分布的哈希
        long total = 0;
        for (int y = 0; y < 32; y++) {
            for (int x = 0; x < 32; x++) {
                total += edgeImage.getRGB(x, y) & 0xFF;
            }
        }
        int avg = (int) (total / (32 * 32));

        long hash = 0;
        for (int y = 0; y < 32; y++) {
            for (int x = 0; x < 32; x++) {
                int pixel = edgeImage.getRGB(x, y) & 0xFF;
                hash = (hash << 1) | (pixel > avg ? 1 : 0);
            }
        }
        return hash;
    }

    /**
     * 缩放图像
     */
    private static BufferedImage resizeImage(BufferedImage original, int width, int height) {
        BufferedImage resized = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
        Graphics2D g = resized.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g.drawImage(original, 0, 0, width, height, null);
        g.dispose();
        return resized;
    }

    /**
     * 日志记录器
     */
    static class Logger {
        private PrintWriter writer;

        public Logger(File logDir) {
            try {
                String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
                File logFile = new File(logDir, "image_match_" + timestamp + ".log");
                writer = new PrintWriter(new FileWriter(logFile));
                log("日志文件: " + logFile.getAbsolutePath());
            } catch (IOException e) {
                System.err.println("无法创建日志文件: " + e.getMessage());
                writer = new PrintWriter(System.out);
            }
        }

        public void log(String message) {
            String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
            String logEntry = "[" + timestamp + "] " + message;
            System.out.println(logEntry);
            writer.println(logEntry);
            writer.flush();
        }

        public void close() {
            if (writer != null) {
                writer.close();
            }
        }
    }
}